from sklearn.tree import DecisionTreeClassifier
from bagging import np


def randomSampling(X, Y):
    """
    含n个样本点的数据集（X,Y），其中 样本特征 X 是形状为（n, num_features）的numpy数组
    现对数据集（X,Y）进行随机有放回采样
    """
    n = len(X)  # 数据集大小

    # 有放回地抽样n_samples次
    indexes_sampling = np.random.choice(np.arange(0, n), size=n, replace=True)

    X_train = X[indexes_sampling]  # 按照行索引取出抽到的样本
    Y_train = Y[indexes_sampling]
    return X_train, Y_train


class RandomForestForClassifing:
    def __init__(self, num_trees=100, max_features=0.2):
        # max_fearures参数很关键，用于控制在寻找最优划分属性时，搜索的最大特征数
        self.treesList = [DecisionTreeClassifier(max_features=max_features) for i in range(num_trees)]
        self.T = num_trees

        self.num_labels = 0  # 可能的标签的数量

    def fit(self, X_train, Y_train):
        self.num_labels = max(Y_train) + 1
        for i in range(self.T):
            X, Y = randomSampling(X_train, Y_train)
            self.treesList[i].fit(X, Y)

    def score(self, X, Y):
        labels = self.predict(X)
        n = float( len(X) )
        acc = (Y == labels).sum() / n
        return acc

    def predict(self, X):
        count_labels = np.zeros(shape=(len(X), self.num_labels))
        i_indexes = np.arange( len(X) )
        for i in range(self.T):
            label = self.treesList[i].predict(X)
            count_labels[i_indexes, label] += 1
        labels = count_labels.argmax(axis=1)
        return labels


if __name__ == "__main__":
    from loadDatas import loadDatas
    from sklearn.ensemble import RandomForestClassifier

    X_train, X_test, y_train, y_test = loadDatas(datasSubject='wine', test_size=0.3)
    forestRI = RandomForestForClassifing(num_trees=100)
    treeModel = DecisionTreeClassifier()
    forestRIModel = RandomForestClassifier()

    print("随机森林复合模型(用户实现)：100棵决策树")
    forestRI.fit(X_train, y_train)
    trainScore = forestRI.score(X_train, y_train)
    testScore = forestRI.score(X_test, y_test)
    print(f'\ttrainScore:{trainScore}\n\ttestScore:{testScore}')

    print("单个决策树模型")
    treeModel.fit(X_train, y_train)
    trainScore = treeModel.score(X_train, y_train)
    testScore = treeModel.score(X_test, y_test)
    print(f'\ttrainScore:{trainScore}\n\ttestScore:{testScore}')

    print("随机森林复合模型(sklearn接口，默认100棵树)")
    forestRIModel.fit(X_train, y_train)
    trainScore = forestRIModel.score(X_train, y_train)
    testScore = forestRIModel.score(X_test, y_test)
    print(f'\ttrainScore:{trainScore}\n\ttestScore:{testScore}')